home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-06-20 | 17.8 KB | 490 lines | [TEXT/3PRM] |
- module draw
-
- import StdEnv
- import deltaEventIO, deltaIOSystem, deltaPicture, deltaWindow, deltaDialog
-
- :: * IO :== IOState State
- :: * State = { tool :: ToolIdType
- , sel_figs :: [Drawable]
- , other_figs :: [Drawable]
- , clip_board :: [Drawable]
- }
- :: ToolIdType :== Int
-
- :: Drawable = E.a:
- { state :: a
- , move :: a -> Point -> a
- , resize :: a Point (Real,Real) -> a
- , draw :: a Picture -> Picture
- , bounds :: a -> Rectangle
- , ungroup :: a -> [Drawable]
- , contains :: a Point -> Bool
- }
-
-
- instance + (a,b) | + a & + b
- where
- (+) (x1,y1) (x2,y2) = (x1 + x2, y1 + y2)
-
- instance - (a,b) | - a & - b
- where
- (-) (x1,y1) (x2,y2) = (x1 - x2, y1 - y2)
-
- (leq) infix 4 :: !(a,b) !(a,b) -> Bool | < a & < b
- (leq) (x1,y1) (x2,y2) = x1 <= x2 && y1 <= y2
-
- InRectangle :: Point Rectangle -> Bool
- InRectangle p (tl, br) = tl leq p && p leq br
-
- ResizeRectangle :: Rectangle Point (Real,Real) -> Rectangle
- ResizeRectangle (tl,br) point (x_fact,y_fact)
- = ( (toInt (toReal tl_x_diff * x_fact), toInt (toReal tl_y_diff * y_fact)) + point
- , (toInt (toReal br_x_diff * x_fact), toInt (toReal br_y_diff * y_fact)) + point
- )
- where
- (tl_x_diff, tl_y_diff) = tl - point
- (br_x_diff, br_y_diff) = br - point
-
-
- normalize ((x1,y1),(x2,y2)) = ((min x1 x2, min y1 y2), (max x1 x2, max y1 y2))
-
- PI :== 3.1415926535898
-
- LineMargin :== 3
-
- MakeLine :: Line -> Drawable
- MakeLine line
- = { state = line
- , draw = DrawLine
- , move = \line dist -> line + (dist, dist)
- , resize = ResizeRectangle
- , bounds = \s -> normalize s
- , ungroup = \s -> []
- , contains = on_line
- }
- where
- on_line line=:((x1,y1),(x2,y2)) (x3,y3)
- = InRectangle (x3,y3) (tl_bound - (LineMargin,LineMargin), br_bound + (LineMargin,LineMargin)) &&
- abs (y_diff * (x3 - x1) - x_diff * (y3 - y1)) <= max (abs (x_diff * LineMargin)) (abs (y_diff * LineMargin))
- where
- (tl_bound, br_bound) = normalize line
-
- x_diff = x2 - x1
- y_diff = y2 - y1
-
- OvalMargin = 3.0
-
- MakeOval :: Oval -> Drawable
- MakeOval oval
- = { state = normalize oval
- , draw = DrawOval
- , move = \oval dist -> oval + (dist, dist)
- , resize = \oval point fact -> normalize (ResizeRectangle oval point fact)
- , bounds = \s -> s
- , ungroup = \s -> []
- , contains = on_oval
- }
- where
- on_oval (tl=:(tl_x,tl_y),br=:(br_x,br_y)) point
- = abs (sqrt sqr_dist_to_centre - toReal x_radius) <= norm_margin
- where
- sqr_dist_to_centre = sqr_x + sqr_y * mul_fact
-
- sqr_y = toReal (y * y)
- sqr_x = toReal (x * x)
-
- sqr_x_radius = toReal (x_radius * x_radius)
- sqr_y_radius = toReal (y_radius * y_radius)
-
- mul_fact = sqr_x_radius / sqr_y_radius
-
- norm_margin = OvalMargin * sqrt (1.0 + mul_fact)
-
- (x,y) = point - (tl_x + x_radius,tl_y + y_radius)
-
- x_radius = max ((br_x - tl_x) / 2) 1
- y_radius = max ((br_y - tl_y) / 2) 1
-
-
- Move :: [Drawable] Point -> [Drawable]
- Move drawables dist = map (\ drawable=:{move,state} -> { drawable & state = move state dist}) drawables
-
- Resize :: [Drawable] Point (Real,Real) -> [Drawable]
- Resize drawables point fact = map (\ drawable=:{resize,state} -> { drawable & state = resize state point fact}) drawables
-
- Draw :: [Drawable] Picture -> Picture
- Draw drawables pict = foldl (\p {draw,state} -> draw state p) pict drawables
-
- Bounds :: [Drawable] -> Rectangle
- Bounds [] = ((0, 0), (0, 0))
- Bounds drawables = foldl combine_bounds bound rest_bounds
- where
- combine_bounds ((r1tlx,r1tly),(r1brx,r1bry)) ((r2tlx,r2tly),(r2brx,r2bry))
- = ((min r1tlx r2tlx,min r1tly r2tly),(max r1brx r2brx,max r1bry r2bry))
-
- [bound:rest_bounds] = map (\{bounds,state} -> bounds state) drawables
-
- Contains :: [Drawable] Point -> Bool
- Contains drawables point = foldr ((||) o \{contains,state} -> contains state point) False drawables
-
- Ungroup :: [Drawable] -> [Drawable]
- Ungroup []
- = []
- Ungroup [drawable=:{ungroup,state} : drawables]
- = case ungroup state of
- [] -> [drawable : Ungroup drawables]
- list -> list ++ Ungroup drawables
-
- MakeRectangle :: Rectangle -> [Line]
- MakeRectangle ((x1,y1),(x2,y2)) = [((x1,y1),(x1,y2)), ((x1,y2),(x2,y2)),((x2,y2),(x2,y1)),((x2,y1),(x1,y1))]
-
- MakeGroup :: [Drawable] -> Drawable
- MakeGroup drawables
- = { state = drawables
- , move = Move
- , resize = Resize
- , draw = Draw
- , bounds = Bounds
- , ungroup = \s -> s
- , contains = Contains
- }
-
- FileId :== 1;
- QuitId :== 11;
- EditId :== 2;
- CutId :== 21;
- CopyId :== 22;
- PasteId :== 23;
- ArrangeId :== 3;
- GroupId :== 31;
- UngroupId :== 32;
- ToolId :== 4;
- SelectToolId :== 41;
- RectangleToolId :== 42;
- LineToolId :== 43;
- OvalToolId :== 44;
-
- InitState = { tool = SelectToolId, other_figs = [fig], sel_figs = [], clip_board = [] }
- where
- rect = MakeGroup (map MakeLine (MakeRectangle ((10,10),(50,50))))
- fig = MakeGroup [rect, line, MakeGroup (map MakeLine (MakeRectangle ((50,70), (110,190))))]
- line = MakeLine ((20,20), (90,80))
-
-
- Start :: * World -> * World
- Start world
- # (events, world) = OpenEvents world
- (_, events) = StartIO [menu, window] InitState [] events
- world = CloseEvents events world
- = world
- where
- menu = MenuSystem [file, edit, arrange, tool]
-
- file = PullDownMenu FileId "File" Able
- [ MenuItem QuitId "Quit" (Key 'Q') Able Quit ]
-
- edit = PullDownMenu EditId "Edit" Able
- [ MenuItem CutId "Cut" (Key 'X') Able Cut
- , MenuItem CopyId "Copy" (Key 'C') Able Copy
- , MenuItem PasteId "Paste" (Key 'V') Able Paste
- ]
-
- arrange = PullDownMenu ArrangeId "Arrange" Able
- [ MenuItem GroupId "Group" (Key 'G') Able DoGroup
- , MenuItem UngroupId "Ungroup" (Key 'U') Able DoUngroup
- ]
-
- tool = PullDownMenu ToolId "Draw" Able
- [ MenuRadioItems SelectToolId
- [ MenuRadioItem SelectToolId "Select" NoKey Able (SetTool SelectToolId StandardCursor)
- , MenuRadioItem RectangleToolId "Rectangle" NoKey Able (SetTool RectangleToolId CrossCursor)
- , MenuRadioItem LineToolId "Line" NoKey Able (SetTool LineToolId CrossCursor)
- , MenuRadioItem OvalToolId "Oval" NoKey Able (SetTool OvalToolId CrossCursor)
- ]
- ]
-
- window = WindowSystem [picture]
- picture = ScrollWindow 1 (0,0) "Picture"
- (ScrollBar (Thumb 0) (Scroll 10)) (ScrollBar (Thumb 0) (Scroll 10))
- ((0,0), (1000,1000)) (50,50) (500,300) Update
- [Mouse Able JerryWaits, GoAway Quit]
-
-
- SetTool :: Int CursorShape State IO -> (State, IO)
- SetTool tool_id cursor state=:{tool} io
- | tool_id == tool = (state, io)
- | otherwise = ({state & tool = tool_id}, ChangeActiveWindowCursor cursor io)
-
- SelMarkerWidth :== 3
-
- MakeSelectionSquare :: Point -> Rectangle
- MakeSelectionSquare p = (p - (SelMarkerWidth-1,SelMarkerWidth-1), p + (SelMarkerWidth,SelMarkerWidth))
-
- Select :: [Drawable] Picture -> Picture
- Select drawables pict
- = foldr (DrawSelectionMarkers o (\{bounds,state} -> bounds state)) pict drawables
- where
- DrawSelectionMarkers :: Rectangle Picture -> Picture
- DrawSelectionMarkers ((tlx,tly), (brx,bry)) pict
- | tlx == brx = DrawSelectionMarker (tlx,tly) (DrawSelectionMarker (tlx,bry) pict)
- | tly == bry = DrawSelectionMarker (tlx,tly) (DrawSelectionMarker (brx,tly) pict)
- | otherwise = DrawSelectionMarker (tlx,tly) (DrawSelectionMarker (tlx,bry)
- (DrawSelectionMarker (brx,bry) (DrawSelectionMarker (brx,tly) pict)))
-
- DrawSelectionMarker :: Point Picture -> Picture
- DrawSelectionMarker p pict = FillRectangle (MakeSelectionSquare p) pict
-
- ClearSelectedFigures :: [Drawable] [Drawable] Picture -> Picture
- ClearSelectedFigures [] not_selected pict
- = pict
- ClearSelectedFigures figures not_selected pict
- = UpdateWindow [update_rect] [] not_selected (EraseRectangle update_rect pict)
- where
- update_rect = Bounds figures + ((1-SelMarkerWidth,1-SelMarkerWidth),(SelMarkerWidth,SelMarkerWidth))
-
-
- Quit state io = (state, QuitIO io)
-
- Copy state=:{sel_figs} io = ({ state & clip_board = Move sel_figs (20,20)}, io)
-
- Cut state=:{sel_figs,other_figs} io
- = ({ state & clip_board = Move sel_figs (20,20), sel_figs = []}, DrawInActiveWindow [ClearSelectedFigures sel_figs other_figs] io)
-
- Paste state=:{clip_board, sel_figs, other_figs} io
- = ({ state & sel_figs = clip_board, other_figs = not_selected }, draw_window)
- where
- not_selected = sel_figs ++ other_figs
- draw_window = DrawInActiveWindow [ ClearSelectedFigures sel_figs not_selected
- , Draw clip_board, SetPenMode XorMode, Select clip_board, SetPenMode CopyMode
- ] io
-
- DoGroup state=:{sel_figs,other_figs} io
- = ({ state & sel_figs = group }, DrawInActiveWindow draw_group io)
- where
- draw_group = [SetPenMode XorMode, Select sel_figs, Select group, SetPenMode CopyMode]
- group = [MakeGroup sel_figs]
-
- DoUngroup state=:{sel_figs,other_figs} io
- = ({ state & sel_figs = group_elems }, DrawInActiveWindow draw_group_elems io)
- where
- draw_group_elems = [SetPenMode XorMode, Select sel_figs, Select group_elems, SetPenMode CopyMode]
- group_elems = Ungroup sel_figs
-
- Update :: UpdateArea State -> (State, [DrawFunction])
- Update area state=:{sel_figs,other_figs}
- = (state, [UpdateWindow area sel_figs other_figs])
-
- UpdateWindow :: UpdateArea [Drawable] [Drawable] Picture -> Picture
- UpdateWindow area selected not_selected pict
- = SetPenMode CopyMode (Select redraw_sel_figs (SetPenMode XorMode (Draw (redraw_other_figs ++ redraw_sel_figs) pict)))
- where
- redraw_sel_figs = DetermineRedraws area selected []
- redraw_other_figs = DetermineRedraws area not_selected []
-
- DetermineRedraws [] drawables selected = selected
- DetermineRedraws [rect:rects] drawables selected
- = DetermineRedraws rects remaining (tobedrawn ++ selected)
- where
- (tobedrawn, remaining) = Split (intersect rect) drawables
-
- intersect :: Rectangle (Drawable) -> Bool
- intersect (tl1,br1) {bounds,state}
- = tl1 leq br2 && tl2 leq br1
- where
- (tl2,br2) = bounds state
-
- RetrieveSelectedFigures :: Point [Drawable] -> ([Drawable], [Drawable])
- RetrieveSelectedFigures point drawables = Split ( \{contains,state} -> contains state point) drawables
-
- RetrieveSurroundedFigures :: Rectangle [Drawable] -> ([Drawable], [Drawable])
- RetrieveSurroundedFigures rect drawables
- = Split (is_surrounding (normalize rect) o \{bounds,state} -> bounds state) drawables
- where
- is_surrounding (r1tl,r1br) (r2tl,r2br) = r1tl leq r2tl && r2br leq r1br
-
- ResizeAreaIsSelected :: Point [Drawable] -> (Bool, Rectangle)
- ResizeAreaIsSelected point [{bounds,state}]
- | InRectangle point (MakeSelectionSquare (tlx,tly)) = (True, ((brx,bry), (tlx,tly)))
- | InRectangle point (MakeSelectionSquare (brx,bry)) = (True, ((tlx,tly), (brx,bry)))
- | InRectangle point (MakeSelectionSquare (tlx,bry)) = (True, ((brx,tly), (tlx,bry)))
- | InRectangle point (MakeSelectionSquare (brx,tly)) = (True, ((tlx,bry), (brx,tly)))
- | otherwise = (False, Omega)
- where
- ((tlx,tly),(brx,bry)) = bounds state
- ResizeAreaIsSelected point _ = (False, Omega)
-
- Split :: (x -> .Bool) .[x] -> (.[x],.[x])
- Split p [] = ([], [])
- Split p [x:xs]
- | p x = ([x:as], bs)
- | otherwise = (as, [x:bs])
- where
- (as, bs) = Split p xs
-
- Omega :: .x
- Omega = abort "tried to access an undefined expression"
-
- DrawBoundingBox :: Rectangle Picture -> Picture
- DrawBoundingBox rect pict = SetPenPattern BlackPattern (foldr DrawLine (SetPenPattern GreyPattern pict) (MakeRectangle rect))
-
- JerryWaits :: MouseState State IO -> (State, IO)
- JerryWaits (pos, ButtonDown, (shift,_,_,_)) state=:{tool, other_figs, sel_figs} io
- | tool <> SelectToolId
- = ( { state & sel_figs = [], other_figs = other_figs ++ sel_figs }
- , ChangeActiveMouseFunction (JerryDraws tool (pos, pos))
- (DrawInActiveWindow [SetPenMode XorMode, Select sel_figs] io)
- )
- | not shift && have_to_resize
- = ( state
- , ChangeActiveMouseFunction (JerryResizes resize_rect pos)
- (DrawInActiveWindow [ SetPenMode XorMode, Select sel_figs
- , SetPenMode OrMode, DrawBoundingBox resize_rect, SetPenMode XorMode
- ] io)
- )
- | Contains sel_figs pos
- = if (shift)
- ( { state & sel_figs = not_selected, other_figs = other_figs ++ selected}
- , DrawInActiveWindow [SetPenMode XorMode, Select selected, SetPenMode CopyMode] io
- )
- ( state
- , ChangeActiveMouseFunction (JerryWaitsForDragging pos sel_figs) io
- )
- with
- (selected, not_selected) = RetrieveSelectedFigures pos sel_figs
- | isEmpty selected
- = if (shift)
- ( state
- , ChangeActiveMouseFunction (JerrySelects (pos, pos)) io
- )
- ( { state & sel_figs = [], other_figs = other_figs ++ sel_figs}
- , ChangeActiveMouseFunction (JerrySelects (pos, pos))
- (DrawInActiveWindow [SetPenMode XorMode, Select sel_figs, SetPenMode CopyMode] io)
- )
- | otherwise
- = if (shift)
- ( { state & sel_figs = selected ++ sel_figs, other_figs = not_selected }
- , ChangeActiveMouseFunction (JerryWaitsForDragging pos (selected ++ sel_figs))
- (DrawInActiveWindow [SetPenMode XorMode, Select selected, SetPenMode CopyMode] io)
- )
- ( { state & sel_figs = selected, other_figs = not_selected ++ sel_figs }
- , ChangeActiveMouseFunction (JerryWaitsForDragging pos selected)
- (DrawInActiveWindow [SetPenMode XorMode, Select (selected ++ sel_figs), SetPenMode CopyMode] io)
- )
- where
- (selected, not_selected) = RetrieveSelectedFigures pos other_figs
- (have_to_resize, resize_rect) = ResizeAreaIsSelected pos sel_figs
-
- JerryWaits mouse state io = (state, io)
-
- JerrySelects :: Rectangle MouseState State IO -> (State, IO)
- JerrySelects rect=:(top_left,bot_right) (pos, ButtonUp, mods) state=:{sel_figs,other_figs} io
- = ( { state & sel_figs = selected ++ sel_figs, other_figs = not_selected }
- , ChangeActiveMouseFunction JerryWaits (DrawInActiveWindow draw_figure io)
- )
- where
- (selected, not_selected) = RetrieveSurroundedFigures (top_left, pos) other_figs
- draw_figure = [ SetPenMode XorMode, DrawRectangle rect, Select selected, SetPenMode CopyMode]
-
- JerrySelects rect=:(top_left, bot_right) (pos, buttondown, mods) state io
- | bot_right == pos
- = (state, io)
- | otherwise
- = (state, ChangeActiveMouseFunction (JerrySelects new_rect) (DrawInActiveWindow draw_tmp_rectangle io))
- where
- new_rect = (top_left, pos)
- draw_tmp_rectangle = [SetPenMode XorMode, DrawRectangle rect, DrawRectangle new_rect]
-
-
- JerryWaitsForDragging :: Point [Drawable] MouseState State IO -> (State, IO)
- JerryWaitsForDragging prev_pos selected (pos, ButtonUp, _) state io
- = (state, ChangeActiveMouseFunction JerryWaits io)
- JerryWaitsForDragging prev_pos selected (pos, _, _) state io
- | prev_pos == pos
- = (state, io)
- | otherwise
- = (state, ChangeActiveMouseFunction (JerryDrags pos moved_figures)
- (DrawInActiveWindow [SetPenMode XorMode, Draw moved_figures, SetPenMode CopyMode] io))
- where
- moved_figures = Move selected (pos - prev_pos)
-
-
- JerryDrags :: Point [Drawable] MouseState State IO -> (State, IO)
- JerryDrags prev_pos selected (pos, ButtonUp, mods) state=:{sel_figs, other_figs} io
- = ( { state & sel_figs = new_figs }, ChangeActiveMouseFunction JerryWaits (DrawInActiveWindow draw_figures io))
- where
- new_figs = Move selected (pos - prev_pos)
- draw_figures = [ SetPenMode XorMode, Draw selected, SetPenMode CopyMode
- , ClearSelectedFigures sel_figs other_figs
- , Draw new_figs, SetPenMode XorMode, Select new_figs, SetPenMode CopyMode
- ]
-
- JerryDrags prev_pos selected (pos, _, mods) state io
- | prev_pos == pos
- = (state, io)
- | otherwise
- = (state, ChangeActiveMouseFunction (JerryDrags pos moved_figures)
- (DrawInActiveWindow [SetPenMode XorMode, Draw (selected ++ moved_figures), SetPenMode CopyMode] io))
- where
- moved_figures = Move selected (pos - prev_pos)
-
-
- DrawFigure :: ToolIdType Rectangle Picture -> Picture
- DrawFigure tool rect pict
- | tool == RectangleToolId = DrawRectangle rect pict
- | tool == LineToolId = DrawLine rect pict
- | otherwise = DrawOval rect pict
-
- MakeFigure :: ToolIdType Rectangle -> Drawable
- MakeFigure tool rect
- | tool == RectangleToolId = MakeGroup (map MakeLine (MakeRectangle rect))
- | tool == LineToolId = MakeLine rect
- | otherwise = MakeOval rect
-
-
- JerryDraws :: ToolIdType Rectangle MouseState State IO -> (State, IO)
- JerryDraws tool rect=:(top_left,bot_right) (pos, ButtonUp, _) state io
- = ({ state & sel_figs = [new_fig] }, ChangeActiveMouseFunction JerryWaits (DrawInActiveWindow draw_figure io))
- where
- new_fig = MakeFigure tool (top_left, pos)
- draw_figure = [ DrawFigure tool rect, SetPenMode CopyMode, Draw [new_fig]
- , SetPenMode XorMode, Select [new_fig], SetPenMode CopyMode
- ]
- JerryDraws tool rect=:(top_left, bot_right) (pos, _ , _) state io
- | bot_right == pos
- = (state, io)
- | otherwise
- = (state, ChangeActiveMouseFunction (JerryDraws tool new_rect) (DrawInActiveWindow draw_figure io))
- where
- new_rect = (top_left, pos)
- draw_figure = [DrawFigure tool rect, DrawFigure tool new_rect]
-
- DetermineMultiplicationFactor :: Point Point Point -> (!Real, !Real)
- DetermineMultiplicationFactor (tlx,tly) (old_brx,old_bry) (new_brx,new_bry)
- = (toReal (new_brx - tlx) / toReal (old_brx - tlx), toReal (new_bry - tly) / toReal (old_bry - tly))
-
- DrawResizeRectangle :: Point Point Point Picture -> Picture
- DrawResizeRectangle (tl_x,tl_y) rs_br=:(rs_br_x,rs_br_y) bo_br=:(bo_br_x,bo_br_y) pict
- = DrawLine ((tl_x, rs_br_y), rs_br) (DrawLine ((rs_br_x, tl_y), rs_br) pict)
-
- JerryResizes :: Rectangle Point MouseState State IO -> (State, IO)
- JerryResizes resize_rect=:(top_left,bot_right) orig_bot_right (pos, ButtonUp, _ ) state=:{sel_figs, other_figs} io
- = ({ state & sel_figs = new_figs }, ChangeActiveMouseFunction JerryWaits (DrawInActiveWindow draw_figure io))
- where
- new_figs = Resize sel_figs top_left (DetermineMultiplicationFactor top_left orig_bot_right pos)
- draw_figure = [ DrawResizeRectangle top_left bot_right orig_bot_right, SetPenMode CopyMode
- , ClearSelectedFigures sel_figs other_figs
- , Draw new_figs
- , SetPenMode XorMode, Select new_figs, SetPenMode CopyMode
- ]
-
- JerryResizes resize_rect=:(top_left,bot_right) orig_bot_right (pos, _, _) state io
- | bot_right == pos
- = (state, io)
- | otherwise
- = (state, ChangeActiveMouseFunction (JerryResizes new_resize_rect orig_bot_right) (DrawInActiveWindow draw_figure io))
- where
- new_resize_rect = (top_left, pos)
- draw_figure = if (orig_bot_right == pos)
- [DrawResizeRectangle top_left pos orig_bot_right]
- [DrawResizeRectangle top_left bot_right orig_bot_right, DrawResizeRectangle top_left pos orig_bot_right]
-